use std::{
    borrow::BorrowMut,
    cell::{Cell, RefCell},
    fmt::Debug,
    path::PathBuf,
};

use rust_embed::{EmbeddedFile, RustEmbed};
use serde::Deserialize;
use thunderdome::{Arena, Index};
use QcRender::resources::{Mesh, Resource};

use super::{audio_clip::AudioClip, material::Material};

pub trait ResourceTrait: Debug {
    fn get(&mut self, file_path: &str) -> Option<Resource>;

    fn get_path(&self) -> PathBuf {
        PathBuf::new()
    }
}

impl<T> ResourceTrait for T
where
    T: RustEmbed + Debug,
{
    fn get(&mut self, file_path: &str) -> Option<Resource> {
        if let Some(file) = Self::get(file_path) {
            Some(Resource {
                name: file_path.to_string(),
                file,
            })
        } else {
            None
        }
    }
}

#[derive(Debug)]
pub struct ResourceManager {
    value: RefCell<Option<Box<dyn ResourceTrait>>>,
    // 储存资源的引用
    materials: RefCell<Arena<Material>>,

    meshes: RefCell<Arena<Mesh>>,
    audio_clips: RefCell<Arena<AudioClip>>,
}

impl ResourceManager {
    pub fn new() -> Self {
        Self {
            value: RefCell::new(None),
            materials: RefCell::new(Arena::new()),
            meshes: RefCell::new(Arena::new()),
            audio_clips: RefCell::new(Arena::new()),
        }
    }

    pub fn set_assets(&self, value: Box<dyn ResourceTrait + 'static>) {
        self.value.replace(Some(value));
    }

    pub fn get_assets_path(&self) -> Option<PathBuf> {
        let res = &*self.value.borrow();

        if let Some(res) = res {
            Some(res.get_path())
        } else {
            None
        }
    }

    pub fn get(&self, name: &str) -> Option<Resource> {
        let res = &mut *self.value.borrow_mut();

        if let Some(res) = res {
            res.get(name)
        } else {
            None
        }
    }

    pub fn get_string(&self, name: &str) -> Option<String> {
        let res = &mut *self.value.borrow_mut();

        if let Some(res) = res {
            res.get(name)
                .map(|res| String::from_utf8(res.file.data.to_vec()).ok())
                .flatten()
        } else {
            None
        }
    }

    pub fn get_struct<T: for<'a> Deserialize<'a> + Debug>(&self, name: &str) -> Option<T> {
        let res = &mut *self.value.borrow_mut();

        if let Some(res) = res {
            if let Some(string) = res
                .get(name)
                .map(|res| String::from_utf8(res.file.data.to_vec()).ok())
                .flatten()
            {
                let v: T = ron::from_str(&string).unwrap();
                println!("ron{:?}", v);
                return ron::from_str(&string).ok();
            }
        }
        None
    }

    pub fn get_json<T: for<'a> Deserialize<'a> + Debug>(&self, name: &str) -> Option<T> {
        let res = &mut *self.value.borrow_mut();

        if let Some(res) = res {
            if let Some(string) = res
                .get(name)
                .map(|res| String::from_utf8(res.file.data.to_vec()).ok())
                .flatten()
            {
      
                return serde_json::from_str(&string).ok();
            }
        }
        None
    }


    pub fn add_material(&self, material: Material) -> Index {
        let materials = &mut *self.materials.borrow_mut();
        materials.insert(material)
    }

    pub fn get_material(&self, index: Index) -> Option<Material> {
        let materials = &*self.materials.borrow();
        materials
            .get_by_slot(index.slot())
            .and_then(|m| Some(m.1.clone()))
    }

    pub fn add_mesh(&self, mesh: Mesh) -> Index {
        let meshes = &mut *self.meshes.borrow_mut();
        meshes.insert(mesh)
    }

    pub fn get_mesh(&self, index: Index) -> Option<Mesh> {
        let meshes = &*self.meshes.borrow();
        meshes
            .get_by_slot(index.slot())
            .and_then(|m| Some(m.1.clone()))
    }

    pub fn add_audio(&self, audio: AudioClip) -> Index {
        let audio_clips = &mut *self.audio_clips.borrow_mut();
        audio_clips.insert(audio)
    }

    pub fn get_audio(&self, index: Index) -> Option<AudioClip> {
        let audio_clips = &*self.audio_clips.borrow();
        audio_clips
            .get_by_slot(index.slot())
            .and_then(|m| Some(m.1.clone()))
    }
}
